#include "SymmAlg.h"

SYMMALG::SYMMALG()
{
    ctx = EVP_CIPHER_CTX_new();
}

SYMMALG::~SYMMALG()
{
    if (ctx != NULL)
    {
        EVP_CIPHER_CTX_free(ctx);
    }
}

uint32_t SYMMALG::alg_black(ALG_TYPE type)
{
    uint32_t black_len[] = {TDES_BLACK_LEN,DES_BLACK_LEN,SM4_BLACK_LEN,AES128_BLACK_LEN};
    if(type > AES128)
    {
        return 0;
    }
    return black_len[type];
}

void xor_data(uint8_t* in, uint8_t* out, uint8_t len)
{
    for (uint8_t i = 0; i < len; ++i)
    {
        out[i] = in[i] ^ out[i];
    }
}

//void SYMMALG::MAC(QString* out, QString in, uint16_t inlen, uint8_t* key, uint8_t* iv, uint32_t alg_mode)
//{
//    uint8_t in_buf[2048] = "", mode = alg_mode & 0xFF;
//    uint8_t data_len[4] = { 8,8,16,16 }, buf[16] = { 0 };
//    QString out_1 = "", out_2 = "";
//    bool flag = false;

//    in.append("80");
//    if ((in.size() / 2) % data_len[mode])
//    {
//        for (uint8_t i = 0; i < (data_len[mode] - (in.size() - data_len[mode])); ++i)
//        {
//            in.append("00");
//        }
//    }
//    MString::QString_uchar(in, in_buf);

//    if (mode == TDES)
//    {
//        flag = true;
//        mode = DES;
//    }

//    for (uint32_t i = 0; i < (uint32_t)((inlen / data_len[mode]) + 1); ++i)
//    {
//        if (i == 0)
//        {
//            xor_data(iv, in_buf, data_len[mode]);
//        }
//        else {
//            xor_data(buf, &in_buf[i * data_len[mode]], data_len[mode]);
//        }
//        MString::uchar_QString(&in_buf[i * data_len[mode]], data_len[mode], out_1);
//        bc_alg(out_2, out_1, data_len[mode], key, NULL, ENCRYPT_MODE | ECB_MODE | mode);
//        MString::QString_uchar(out_2, buf);
//    }

//    if (flag == true)
//    {
//        MString::uchar_QString(buf, data_len[mode], out_1);
//        bc_alg(out_2, out_1, data_len[mode], &key[8], NULL, DECODE_MODE | ECB_MODE | mode);

//        bc_alg(out_1, out_2, data_len[mode], key, NULL, ENCRYPT_MODE | ECB_MODE | mode);
//    }
//    out->clear();
//    out->append(out_1);
//    return;
//}



CString SYMMALG::alg_encrypt(const CString &data, const CString &key, const CString &iv, const ALG_TYPE type, const ALG_MODE mode)
{
    uint8_t in_buf[ALG_MAX_LEN] = {0}, out_buf[ALG_MAX_LEN] = {0}, uintkey[ALG_KEY_MAX_LEN] = {0},uintiv[ALG_IV_MAX_LEN] = {0};
    int out_len = 0,in_len = 0;
    CString str = data;
    str.subspace();
    in_len = str.size() / 2;
    if((str.size() / 2) % alg_black(type))
    {
        for(uint8_t i = 0;i < (alg_black(type) - ((str.size() / 2) % alg_black(type)));++i)
        {
            str.append("00");
        }
    }
    str.touint8(in_buf);
    str = key;
    str.subspace();
    str.touint8(uintkey);
    str = iv;
    str.subspace();
    str.touint8(uintiv);

    if(alg_encrypt(in_buf,in_len,uintkey,uintiv,out_buf,out_len,type,mode) == false)
    {
        str = "";
        return str;
    }

    str = CString(out_buf,out_len);
    return str;
}

CString SYMMALG::alg_decrypt(const CString &data, const CString &key, const CString &iv, const ALG_TYPE type, const ALG_MODE mode)
{
    uint8_t in_buf[ALG_MAX_LEN] = {0}, out_buf[ALG_MAX_LEN] = {0}, uintkey[ALG_KEY_MAX_LEN] = {0},uintiv[ALG_IV_MAX_LEN] = {0};
    int out_len = 0,in_len = 0;
    CString str = data;
    str.subspace();
    in_len = str.size() / 2;
    if((str.size() / 2) % alg_black(type))
    {
        str = "";
        return str;
    }
    str.touint8(in_buf);
    str = key;
    str.subspace();
    str.touint8(uintkey);
    str = iv;
    str.subspace();
    str.touint8(uintiv);

    if(alg_decrypt(in_buf,in_len,uintkey,uintiv,out_buf,out_len,type,mode) == false)
    {
        str = "";
        return str;
    }

    str = CString(out_buf, out_len);
    return str;
}

bool SYMMALG::alg_encrypt(unsigned char *data, int data_len, unsigned char *key, unsigned char *iv,
                          unsigned char *out, int &out_len, const ALG_TYPE type, const ALG_MODE mode)
{
    switch (type) {
    case TDES:
        for (uint8_t i = 0; i < DES3_ONE_KEY_LEN; ++i)
        {
            key[i + DES3_ONE_KEY_LEN + DES3_ONE_KEY_LEN] = key[i];
        }
        if (mode == ECB)
        {
            EVP_EncryptInit_ex(ctx, EVP_des_ede3_ecb(), NULL, (unsigned char*)key, NULL);
        }
        else {

            EVP_EncryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, (unsigned char*)key, (unsigned char*)iv);
        }
        break;
        case DES:
            for (uint8_t i = 0; i < DES3_ONE_KEY_LEN; ++i)
            {
                key[i + DES3_ONE_KEY_LEN] = key[i];
            }
            for (uint8_t i = 0; i < DES3_ONE_KEY_LEN; ++i)
            {
                key[i + DES3_ONE_KEY_LEN + DES3_ONE_KEY_LEN] = key[i];
            }
            if (mode == ECB)
            {
                EVP_EncryptInit_ex(ctx, EVP_des_ede_ecb(), NULL, (unsigned char*)key, NULL);
            }
            else {
                EVP_EncryptInit_ex(ctx, EVP_des_ede_cbc(), NULL, (unsigned char*)key, (unsigned char*)iv);
            }
            break;

        case SM4:
            if (mode == ECB)
            {
                EVP_EncryptInit_ex(ctx, EVP_sm4_ecb(), NULL, (unsigned char*)key, NULL);
            }
            else {
                EVP_EncryptInit_ex(ctx, EVP_sm4_cbc(), NULL, (unsigned char*)key, (unsigned char*)iv);
            }
            break;
        case AES128:
            if (mode == ECB)
            {
                EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, (unsigned char*)key, NULL);
            }
            else {
                EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, (unsigned char*)key, (unsigned char*)iv);
            }
            break;
        default:
            return false;
        }

        EVP_CIPHER_CTX_set_padding(ctx, 0); // 不启用PKCS#7填充

        if (1 != EVP_EncryptUpdate(ctx, (unsigned char*)out, &out_len, data, data_len))
        {
            return false;
        }

        if (1 != EVP_EncryptFinal_ex(ctx, out + out_len, &out_len)) {
            return false;
        }
        return true;
}

bool SYMMALG::alg_decrypt(unsigned char *data, int data_len, unsigned char *key, unsigned char *iv, unsigned char *out, int &out_len, const ALG_TYPE type, const ALG_MODE mode)
{
    switch (type) {
    case TDES:
        for (uint8_t i = 0; i < DES3_ONE_KEY_LEN; ++i)
        {
            key[i + DES3_ONE_KEY_LEN + DES3_ONE_KEY_LEN] = key[i];
        }
        if (mode == ECB)
        {
            EVP_DecryptInit_ex(ctx, EVP_des_ede3_ecb(), NULL, (unsigned char*)key, NULL);
        }
        else {
            EVP_DecryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, (unsigned char*)key, (unsigned char*)iv);
        }
        break;
    case DES:
        for (uint8_t i = 0; i < DES3_ONE_KEY_LEN; ++i)
        {
            key[i + DES3_ONE_KEY_LEN] = key[i];
        }
        for (uint8_t i = 0; i < DES3_ONE_KEY_LEN; ++i)
        {
            key[i + DES3_ONE_KEY_LEN + DES3_ONE_KEY_LEN] = key[i];
        }
        if (mode == ECB)
        {
            EVP_DecryptInit_ex(ctx, EVP_des_ede_ecb(), NULL, (unsigned char*)key, NULL);
        }
        else {
            EVP_DecryptInit_ex(ctx, EVP_des_ede_cbc(), NULL, (unsigned char*)key, (unsigned char*)iv);
        }
        break;

    case SM4:
        if (mode == ECB)
        {
            EVP_DecryptInit_ex(ctx, EVP_sm4_ecb(), NULL, (unsigned char*)key, NULL);
        }
        else {
            EVP_DecryptInit_ex(ctx, EVP_sm4_cbc(), NULL, (unsigned char*)key, (unsigned char*)iv);
        }
        break;
    case AES128:
        if (mode == ECB)
        {
            EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, (unsigned char*)key, NULL);
        }
        else {
            EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, (unsigned char*)key, (unsigned char*)iv);
        }
        break;
    default:
        return false;
    }

    EVP_CIPHER_CTX_set_padding(ctx, 0); // 不启用PKCS#7填充

    if (1 != EVP_DecryptUpdate(ctx, out, &out_len, data, data_len))
    {
        return false;
    }

    if (1 != EVP_DecryptFinal_ex(ctx, out + out_len, &out_len)) {
        return false;
    }
    return true;
}



CString SYMMALG::alg_mac(const CString &data, const CString &key, const CString &iv, ALG_TYPE type)
{
    uint8_t in_buf[ALG_MAX_LEN] = {0}, uintkey[ALG_KEY_MAX_LEN] = {0}, uintiv[ALG_IV_MAX_LEN] = {0},
            black_len = alg_black(type),buf[ALG_BLACK_MAC_LEN] = {0};
    int out_len = 0,in_len = 0,app_len = 0;
    bool flag = false;
    CString str = key;
    str.subspace();
    str.touint8(uintkey);
    str = iv;
    str.subspace();
    str.touint8(uintiv);

    str = data;
    str.subspace();
    str.append("80");

    if((str.size() / 2) % black_len)
    {
        app_len = black_len - ((str.size() / 2) % black_len);
        for(int i = 0;i < app_len;++i)
        {
            str.append("00");
        }
    }
    in_len = str.size() / 2;
    str.touint8(in_buf);

    if (type == TDES)
    {
        flag = true;
        type = DES;
    }

    for (uint32_t i = 0; i < (uint32_t)((in_len / black_len) + 1); ++i)
    {
        if (i == 0)
        {
            xor_data(uintiv, in_buf, black_len);
        }
        else {
            xor_data(buf, &in_buf[i * black_len], black_len);
        }

        alg_encrypt(&in_buf[i * black_len], black_len, uintkey, uintiv, buf, out_len, type, ECB);
    }

    if (flag == true)
    {
        alg_encrypt(buf, black_len, &uintkey[8], uintiv, buf, out_len, type, ECB);
        alg_encrypt(buf, black_len, uintkey, uintiv, buf, out_len, type, ECB);
    }
    str.clear();
    str = CString(buf,black_len);
    return str;
}
